package com.weijun.keepalive_daemon

import android.annotation.SuppressLint
import android.app.Application
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.core.app.NotificationCompat
import com.weijun.keepalive_daemon.callback.DaemonCallback
import com.weijun.keepalive_daemon.callback.DaemonNotifyConfiguration
import com.weijun.keepalive_daemon.process.Daemon
import com.weijun.keepalive_daemon.process.DaemonConfigs
import com.weijun.keepalive_daemon.receiver.ScreenStatusManager
import com.weijun.keepalive_daemon.service.CoreService
import com.weijun.keepalive_daemon.service.DaemonJobService
import com.weijun.keepalive_daemon.service.DaemonService
import com.weijun.keepalive_daemon.service.IntentJobService
import com.weijun.keepalive_daemon.utils.ActivityUtils
import java.util.concurrent.TimeUnit

/**
 * @author: weijun
 * date: 2023/7/6
 * description:
 */
object DaemonKeepAlive {

    private var initFlag = true

    var onePxEnabled = true
    var musicPlayEnabled = true
    var daemonCallback: DaemonCallback ?= null //守护进程回调
    @SuppressLint("StaticFieldLeak")
    var daemonNotifyConfiguration:DaemonNotifyConfiguration ?= null

    var application:Application ?= null
        private set
        get() {
            if (field == null) {
                field = getCurApplication()
            }
            return field
        }

    fun init(application: Application) {
        this.application = application
    }

    fun startActivity(context: Context, intent: Intent?) {
        if (ActivityUtils.isAppRunningForeground(context)) {
            context.startActivity(intent)
            return
        }
        ActivityUtils.hookJumpActivity(context, intent)
    }

    fun startWork(){
        if (!initFlag) {
            return
        }
        initFlag = false
        application?.let { app ->
            ScreenStatusManager.register(app) //屏幕状态监听
            Daemon(
                DaemonConfigs(
                    PERSISTENT_CONFIG = DaemonConfigs.Config(
                        processName = "android.process.media",
                        serviceName = DaemonServicePath,
                        receiverName = "",
                        activityName = "",
                    ),
                    DAEMON_ASSISTANT_CONFIG = DaemonConfigs.Config(
                        processName = "${app.packageName}:service",
                        serviceName = CoreServicePath,
                        receiverName = "",
                        activityName = "",
                    )
                )
            ).initDaemon()
            application?.let {
                "DaemonKeepAlive startService".logE()
                startCoreService(it)
                startDaemonService(it)
                startScheduleService(it)
            }
        }
    }

    internal fun startCoreService(context: Context){
        try {
            context.let {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    it.startForegroundService(Intent(it, CoreService::class.java))
                } else {
                    it.startService(Intent(it, CoreService::class.java))
                }
            }
        } catch (ignore: Exception) { }
    }

    internal fun startDaemonService(context: Context){
        try {
            context.let {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    it.startForegroundService(Intent(it, DaemonService::class.java))
                } else {
                    it.startService(Intent(it, DaemonService::class.java))
                }
            }
        } catch (ignore: Exception) { }
    }

    internal fun startScheduleService(context: Context){
        context.let {
            val jobScheduler = it.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
            val builder = JobInfo.Builder(DaemonJobService.JOB_ID, ComponentName(it, DaemonJobService::class.java))
                .setPersisted(true)
            val time = TimeUnit.SECONDS.toMillis(8)
            if (Build.VERSION.SDK_INT < 24) {
                builder.setPeriodic(time)
            } else {
                builder.setMinimumLatency(time)
            }
            try {
                jobScheduler.schedule(builder.build())
            } catch (ignore: Exception) { }
        }
    }

    fun startIntentJobService(context: Context, intent: Intent?, z: Boolean) {
        val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
        val persisted = JobInfo.Builder(IntentJobService.JOB_ID, ComponentName(context, IntentJobService::class.java))
            .setPersisted(false)
            .apply {
                if (Build.VERSION.SDK_INT >= 28) {
                    setImportantWhileForeground(true)
                }
                setRequiresDeviceIdle(false)
                setOverrideDeadline(3000)
            }
        if (Build.VERSION.SDK_INT >= 26 && intent != null) {
            persisted.setTransientExtras(Bundle().apply {
                putParcelable("android.intent.extra.INTENT", intent)
                putBoolean(IntentJobService.IS_ACTIVITY, z)
            })
        }
        try {
            jobScheduler.schedule(persisted.build())
        } catch (ignore: Exception) { }
    }

    private fun getCurApplication(): Application? {
        var application: Application? = null
        try {
            val atClass = Class.forName("android.app.ActivityThread")
            val currentApplicationMethod = atClass.getDeclaredMethod("currentApplication")
            currentApplicationMethod.isAccessible = true
            application = currentApplicationMethod.invoke(null) as Application?
        } catch (e: Exception) {
            e.printStackTrace()
        }
        if (application != null) {
            return application
        }
        try {
            val atClass = Class.forName("android.app.AppGlobals")
            val currentApplicationMethod = atClass.getDeclaredMethod("getInitialApplication")
            currentApplicationMethod.isAccessible = true
            application = currentApplicationMethod.invoke(null) as Application?
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return application
    }
}